﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Package;
using Microsoft.VisualStudio.TextManager.Interop;
using Evaluant.PS4VS.Parser;

namespace Evaluant.PS4VS.Language
{
    static class Configuration
    {
        static CommentInfo commentInfo;
        public static CommentInfo MyCommentInfo { get { return commentInfo; } }

        static Configuration()
        {
            commentInfo.BlockEnd = "*/";
            commentInfo.BlockStart = "/*";
            commentInfo.LineStart = "#";
            commentInfo.UseLineComments = false;

            // default colors - currently, these need to be declared
            CreateColor("Keyword", COLORINDEX.CI_BLUE, COLORINDEX.CI_USERTEXT_BK);
            CreateColor("Comment", COLORINDEX.CI_DARKGREEN, COLORINDEX.CI_USERTEXT_BK);
            CreateColor("Identifier", COLORINDEX.CI_AQUAMARINE, COLORINDEX.CI_USERTEXT_BK, true, false);
            CreateColor("String", COLORINDEX.CI_MAROON, COLORINDEX.CI_USERTEXT_BK);
            CreateColor("Number", COLORINDEX.CI_MAGENTA, COLORINDEX.CI_USERTEXT_BK);
            CreateColor("Text", COLORINDEX.CI_SYSPLAINTEXT_FG, COLORINDEX.CI_USERTEXT_BK);
            TokenColor variable = CreateColor("Variable", COLORINDEX.CI_PURPLE, COLORINDEX.CI_USERTEXT_BK, true, false);
            TokenColor error = CreateColor("Error", COLORINDEX.CI_RED, COLORINDEX.CI_USERTEXT_BK, false, true);
            TokenColor Operator = CreateColor("Operator", COLORINDEX.CI_DARKGRAY, COLORINDEX.CI_USERTEXT_BK);
            //
            // map tokens to color classes
            //

            //Operators
            ColorToken((int)Tokens.AMPAMP, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.AND, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.AS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.BAND, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.BARBAR, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.BOR, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CASESENSITIVE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CCONTAINS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CEQ, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CGE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CGT, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CLE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CLIKE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CLT, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CMATCH, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CNE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CNOTCONTAINS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CNOTLIKE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CNOTMATCH, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CONTAINS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.CREPLACE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.EQ, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.ERRORTOAPPENDFILE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.ERRORTOFILE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.ERRORTOSTDOUT, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.EXACT, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.FILE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.FILETOSTDIN, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.FORMATOPERATOR, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.GE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.GT, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.HASHOPERATOR, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.ICONTAINS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.IEQ, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.IGE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.IGT, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.ILE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.ILIKE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.ILT, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.IMATCH, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.INE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.INOTCONTAINS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.INOTLIKE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.INOTMATCH, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.IREPLACE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.IS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.ISNOT, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.LE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.LIKE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.LT, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.MATCH, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.MINUSMINUS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.NE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.NOTCONTAINS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.NOTLIKE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.NOTMATCH, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.OR, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.PIPE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.PLUSPLUS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.RANGEOPERATOR, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.REDIRECTIONOPERATOR, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.REGEX, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.REPLACE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.SELFDIV, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.SELFMINUS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.SELFMOD, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.SELFMULT, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.SELFPLUS, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.STDOUTTOAPPENDFILE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.STDOUTTOFILE, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.UNARYOPERATOR, TokenType.Operator, Operator, TokenTriggers.None);
            ColorToken((int)Tokens.WILDCARD, TokenType.Operator, Operator, TokenTriggers.None);

            //Keywords
            ColorToken((int)Tokens.KWBEGIN, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWBREAK, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWCONTINUE, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWDEFAULT, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWDO, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWELSE, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWELSEIF, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWEND, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWFINALLY, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWFOR, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWFOREACH, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWIF, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWIN, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWPROCESS, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWRETURN, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWSWITCH, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWTRAP, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWUNTIL, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)Tokens.KWWHILE, TokenType.Keyword, TokenColor.Keyword, TokenTriggers.None);
            ColorToken((int)'(', TokenType.Delimiter, TokenColor.Text, TokenTriggers.MatchBraces);
            ColorToken((int)')', TokenType.Delimiter, TokenColor.Text, TokenTriggers.MatchBraces);
            ColorToken((int)'{', TokenType.Delimiter, TokenColor.Text, TokenTriggers.MatchBraces);
            ColorToken((int)'}', TokenType.Delimiter, TokenColor.Text, TokenTriggers.MatchBraces);

            ColorToken((int)Tokens.VARIABLE, TokenType.Identifier, variable, TokenTriggers.Parameter);
            ColorToken((int)Tokens.IDENTIFIER, TokenType.Identifier, TokenColor.Identifier, TokenTriggers.ParameterStart | TokenTriggers.MemberSelect | TokenTriggers.MethodTip);
            ColorToken((int)Tokens.UIDENTIFIER, TokenType.Identifier, TokenColor.Text, TokenTriggers.MemberSelect);
            ColorToken((int)Tokens.PARAMETER, TokenType.Identifier, Operator, TokenTriggers.Parameter);
            ColorToken((int)Tokens.LEX_WHITE, TokenType.WhiteSpace, TokenColor.Text, TokenTriggers.ParameterEnd);

            //Strings
            ColorToken((int)Tokens.LITERALSTRING, TokenType.String, TokenColor.String, TokenTriggers.None);
            ColorToken((int)Tokens.EXPANDABLESTRING, TokenType.String, TokenColor.String, TokenTriggers.MatchBraces);
            ColorToken((int)Tokens.EXPANDABLESTRINGCONTENT, TokenType.String, TokenColor.String, TokenTriggers.None);
            ColorToken((int)Tokens.EXPANDABLESTRINGVARIABLE, TokenType.String, TokenColor.String, TokenTriggers.None);

            //// Extra token values internal to the scanner
            ColorToken((int)Tokens.LEX_ERROR, TokenType.Text, error, TokenTriggers.None);
            ColorToken((int)Tokens.LEX_COMMENT, TokenType.Text, TokenColor.Comment, TokenTriggers.None);

        }

        static List<Microsoft.VisualStudio.TextManager.Interop.IVsColorableItem> colorableItems = new List<Microsoft.VisualStudio.TextManager.Interop.IVsColorableItem>();
        public static IList<Microsoft.VisualStudio.TextManager.Interop.IVsColorableItem> ColorableItems
        {
            get { return colorableItems; }
        }

        public static string Name
        {
            get
            {
                return "PS4VS";
            }
        }

        public static string Extension
        {
            get
            {
                return ".ps1";
            }
        }

        public static TokenColor CreateColor(string name, COLORINDEX foreground, COLORINDEX background)
        {
            return CreateColor(name, foreground, background, false, false);
        }

        public static TokenColor CreateColor(string name, COLORINDEX foreground, COLORINDEX background, bool bold, bool strikethrough)
        {
            colorableItems.Add(new ColorableItem(name, foreground, background, bold, strikethrough));
            return (TokenColor)colorableItems.Count;
        }

        public static void ColorToken(int token, TokenType type, TokenColor color, TokenTriggers trigger)
        {
            definitions[token] = new TokenDefinition(type, color, trigger);
        }

        public static TokenDefinition GetDefinition(int token)
        {
            TokenDefinition result;
            return definitions.TryGetValue(token, out result) ? result : defaultDefinition;
        }

        private static TokenDefinition defaultDefinition = new TokenDefinition(TokenType.Text, TokenColor.Text, TokenTriggers.None);
        private static Dictionary<int, TokenDefinition> definitions = new Dictionary<int, TokenDefinition>();

        public struct TokenDefinition
        {
            public TokenDefinition(TokenType type, TokenColor color, TokenTriggers triggers)
            {
                this.TokenType = type;
                this.TokenColor = color;
                this.TokenTriggers = triggers;
            }

            public TokenType TokenType;
            public TokenColor TokenColor;
            public TokenTriggers TokenTriggers;
        }
    }

    public class ColorableItem : Microsoft.VisualStudio.TextManager.Interop.IVsColorableItem
    {
        private string displayName;
        private COLORINDEX background;
        private COLORINDEX foreground;
        private uint fontFlags = (uint)FONTFLAGS.FF_DEFAULT;

        public ColorableItem(string displayName, COLORINDEX foreground, COLORINDEX background, bool bold, bool strikethrough)
        {
            this.displayName = displayName;
            this.background = background;
            this.foreground = foreground;

            if (bold)
                this.fontFlags = this.fontFlags | (uint)FONTFLAGS.FF_BOLD;
            if (strikethrough)
                this.fontFlags = this.fontFlags | (uint)FONTFLAGS.FF_STRIKETHROUGH;
        }

        #region IVsColorableItem Members
        public int GetDefaultColors(COLORINDEX[] piForeground, COLORINDEX[] piBackground)
        {
            if (null == piForeground)
            {
                throw new ArgumentNullException("piForeground");
            }
            if (0 == piForeground.Length)
            {
                throw new ArgumentOutOfRangeException("piForeground");
            }
            piForeground[0] = foreground;

            if (null == piBackground)
            {
                throw new ArgumentNullException("piBackground");
            }
            if (0 == piBackground.Length)
            {
                throw new ArgumentOutOfRangeException("piBackground");
            }
            piBackground[0] = background;

            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int GetDefaultFontFlags(out uint pdwFontFlags)
        {
            pdwFontFlags = this.fontFlags;
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }

        public int GetDisplayName(out string pbstrName)
        {
            pbstrName = displayName;
            return Microsoft.VisualStudio.VSConstants.S_OK;
        }
        #endregion

    }
}
